import { Bluetooth, transferDataFn, EmitEvent, EventDataMap } from './bluetooth';

export enum Operation {
  READ = 'READ', // 读取
  WRITE = 'WRITE', // 写入
  TERMINATE = 'TERMINATE', // 终止
  DATA_TRANSFER = 'DATA_TRANSFER', // 上行或下行数据传输
  DATA_ACK = 'DATA_ACK', // 终止数据
  GET_FLAG = 'GET_FLAG',
  SET_FLAG = 'SET_FLAG',
  HOST_AUTH = 'HOST_AUTH', // 主机认证
  DEVICE_AUTH = 'DEVICE_AUTH' // 设备认证
}
/** 三模通用遥控器信息类型（严格符合标准文档定义） */
export enum RemoteControllerInfoType {
  HARDWARE = 'rel0', // 硬件信息
  FIRMWARE = 'fw01', // 固件信息
  VERSION = 'usr1', // 升级数据版本信息
  AUTH_CODE = 'usr2', // 认证识别码
  IR_TV = 'irtv', // 电视机红外遥控信息
  IR_STB = 'irstb', // 机顶盒红外遥控信息
  BLE_TV = 'bletv', // 电视机蓝牙/星闪遥控信息
  BLE_STB = 'blestb' // 机顶盒蓝牙/星闪遥控信息
}
/** 读取/写入长度选项（严格限制标准值） */
export type DataLength = 16 | 32 | 64 | 128;
/** 操作状态码 */
export enum OperationStatus {
  SUCCESS = 0x00,
  IN_PROGRESS = 0xf0,
  NOT_FOUND = 0xf2,
  FAILED = 0xff
}
/** 升级标志状态 */
export enum UpgradeFlag {
  NONE = 0x00000000,
  VALID = 0xf5f5f500
}

/** 认证状态码 */
export enum AuthStatus {
  INITIATE = 0x00,
  SUCCESS = 0x01,
  FAILED = 0x02
}

export enum CommandCode {
  SET_CONTROL_TARGET = 0x01,
  SET_TV_SOURCE_MODE = 0x02
}

export type OperationParamMap = {
  [Operation.READ]: {
    length: DataLength; // 每次读取长度 (16/32/64/128)
    type: RemoteControllerInfoType; // 信息类型
  };
  [Operation.WRITE]: {
    length: DataLength; // 每次读取长度 (16/32/64/128)
    type: RemoteControllerInfoType; // 信息类型
    dataLength: number; // 数据长度
  };
  [Operation.TERMINATE]: {
    crc: boolean;
  }; // 终止操作
  [Operation.DATA_TRANSFER]: {
    packetSequence: number; // 包序列号 0x00～0xFF
    dataLength: number; // 每次传输的有效数据长度 0x01～len
    data: Uint8Array; // 数据内容
  }; // 数据传输
  [Operation.DATA_ACK]: {};
  [Operation.GET_FLAG]: {}; // 获取升级标志
  [Operation.SET_FLAG]: {
    flag: UpgradeFlag; // 升级标志
  };
  [Operation.HOST_AUTH]: {
    status: AuthStatus; // 认证识别码
    keyIndex: number; // Key index
    random: Uint8Array; // 随机数
    idCode: string; // ID 码
  }; // 主机认证
  [Operation.DEVICE_AUTH]: {
    status: AuthStatus; // 认证识别码
    keyIndex: number; // Key index
    random: Uint8Array; // 随机数
  };
};
export type OperationResponseMap = {
  [Operation.READ]: {
    status: OperationStatus; // 操作状态码
    address: number; // 地址
    length: number; // 数据长度
  };
  [Operation.WRITE]: {
    status: OperationStatus; // 操作状态码
    address: number; // 地址
    length: number; // 数据长度
  };
  [Operation.TERMINATE]: {
    status: OperationStatus; // 操作状态码
    crc16: number; // CRC16 校验值
  };
  [Operation.DATA_TRANSFER]: {
    packetSequence: number; // 包序列号 0x00～0xFF
    dataLength: number; // 每次传输的有效数据长度 0x01～len
    packetData: Uint8Array; // 数据内容 0x01～len
  };
  [Operation.DATA_ACK]: {};
  [Operation.GET_FLAG]: {
    status: OperationStatus; // 操作状态码
    flag: UpgradeFlag; // 升级标志
  };
  [Operation.SET_FLAG]: {
    status: OperationStatus; // 操作状态码
  };
  [Operation.HOST_AUTH]: {
    hash: Uint8Array; // 16 bytes
  };
  [Operation.DEVICE_AUTH]: {
    hash: Uint8Array; // 16 bytes
  };
};
/** 协议常量定义 */
const PROTOCOL = {
  COMMAND: {
    REQUEST: 0xf9,
    RESPONSE: 0xfa
  },
  OPERATION: {
    READ: 0x11,
    WRITE: 0x12,
    TERMINATE: 0x13,
    DATA_TRANSFER: 0x14,
    DATA_ACK: 0x18,
    GET_FLAG: 0x15,
    SET_FLAG: 0x16,
    HOST_AUTH: 0x20,
    DEVICE_AUTH: 0x21,
    CONFIG: 0x22
  },
  TYPE_LENGTH: 8
};

class BasicRemoteBluetooth extends Bluetooth {
  private _serviceUUID?: string;
  private _characteristicUUID?: string;

  // ============ 公共方法 ============
  /**
   * 配置蓝牙服务参数
   * @param serviceUUID 服务UUID
   * @param characteristicUUID 特征UUID
   */
  configure(serviceUUID?: string, characteristicUUID?: string): this {
    if (serviceUUID) {
      this._serviceUUID = serviceUUID;
    }
    if (characteristicUUID) {
      this._characteristicUUID = characteristicUUID;
    }
    return this;
  }

  async startCommand<T extends Operation>(operation: T, params: OperationParamMap[T]): Promise<void> {
    this._validateConfiguration();
    const buffer = this._buildRequest(operation, params);
    if (!buffer) {
      throw new Error(`Failed to build request buffer [operation]:${operation}`);
    }
    await this.write(this._serviceUUID!, this._characteristicUUID!, buffer);
  }

  /**
   * 解析启动读响应（E.1.1.3.2）
   */
  parseResponse<T extends Operation>(data: ArrayBuffer, operation: T): OperationResponseMap[T] {
    const view = new DataView(data);
    // const opCode = operation === Operation.DATA_TRANSFER ? PROTOCOL.OPERATION.DATA_ACK : PROTOCOL.OPERATION[operation];
    this._validateResponseHeader(view, PROTOCOL.OPERATION[operation]);
    switch (operation) {
      case Operation.READ:
      case Operation.WRITE:
        return {
          status: view.getUint8(2),
          address: view.getUint32(3, false),
          length: view.getUint32(7, false)
        } as OperationResponseMap[T];
      case Operation.TERMINATE:
        const crc16High = view.getUint8(3);
        const crc16Low = view.getUint8(4);
        const crc16 = (crc16High << 8) | crc16Low;
        return {
          status: view.getUint8(2),
          crc16: crc16
        } as OperationResponseMap[T];
      case Operation.DATA_TRANSFER:
        const packetSequence = view.getUint8(2);
        const dataLength = view.getUint8(3);
        let packetData = new Uint8Array(0);
        if (dataLength > 0) {
          packetData = new Uint8Array(data.slice(4, 4 + dataLength));
        }
        return {
          packetSequence: packetSequence,
          packetData, // 数据内容
          dataLength
        } as OperationResponseMap[T];
      case Operation.GET_FLAG:
        return {
          status: view.getUint8(2),
          flag: view.getUint32(3, false)
        } as OperationResponseMap[T];
      case Operation.SET_FLAG:
        return {
          status: view.getUint8(2)
        } as OperationResponseMap[T];
      case Operation.HOST_AUTH:
        return {
          hash: new Uint8Array(data.slice(2, 18)) // 16 bytes
        } as OperationResponseMap[T];
      case Operation.DEVICE_AUTH:
        return {
          hash: new Uint8Array(data.slice(2, 18)) // 16 bytes
        } as OperationResponseMap[T];
      default:
        throw new Error(`Unsupported operation: ${operation}`);
    }
  }

  // fa 14 00 0f 57 f6 00 0b 0a 66 77 3a 56 30 32 2e 36 36 00

  executeCommandWithResponse<T extends Operation>(operation: T, params: OperationParamMap[T], timeout: number = 5000) {
    return new Promise<EventDataMap[EmitEvent.data]>((resolve, reject) => {
      const timer = setTimeout(() => {
        reject(new Error(`${operation} response timeout after ${timeout}ms`));
      }, timeout);
      // 使用once确保只监听一次
      this.once(EmitEvent.data, (data) => {
        clearTimeout(timer);
        console.log(`${operation} response:`, data);
        resolve(data);
      });
      // 执行命令
      this.startCommand(operation, params).catch(reject);
    });
  }

  sendRemoteData(bigData: Uint8Array, awaitResponse: boolean = true) {
    return new Promise<void>(async (resolve) => {
      const fn: transferDataFn = async (params) => {
        const { isLastChunk, ...rest } = params;
        const res: any = await this.executeCommandWithResponse(Operation.DATA_TRANSFER, rest);
        const { dataLength, packetSequence } = this.parseResponse(res.data, Operation.DATA_TRANSFER);
        if (rest.dataLength !== dataLength) {
          throw new Error(`dataLength error: ${rest.dataLength} !== ${dataLength}`);
        }
        if (rest.packetSequence !== packetSequence) {
          throw new Error(`packetSequence error: ${rest.packetSequence} !== ${packetSequence}`);
        }
        if (isLastChunk) {
          if (awaitResponse) {
            this.once(EmitEvent.data, (data) => {
              console.log(`数据传输完毕响应`, data);
              resolve();
            });
          } else {
            resolve();
          }
        }
      };
      await this.send(bigData, fn);
    });
  }

  readNextResponse(split = true, hasCrc = false) {
    return new Promise<ArrayBuffer>((resolve, reject) => {
      if (split) {
        const onNextData = (data: EventDataMap[EmitEvent.data]) => {
          const res = this.parseResponse(data.value, Operation.DATA_TRANSFER);
          console.log(res)
          console.log(this.arrayBufferToHexString(data.value))
          const fullData = this.packetSplitter.processReceivedPacket(
            {
              sequence: res.packetSequence,
              length: res.dataLength,
              data: res.packetData
            },
            data.deviceId,
            hasCrc
          );
          if (fullData) {
            this.off(EmitEvent.data, onNextData); // 移除监听器
            if (fullData.errors.length > 0) {
              reject(new Error(fullData.errors.join(',')));
            } else {
              resolve(fullData.data);
            }
          }
        };
        this.on(EmitEvent.data, onNextData);
      } else {
        this.once(EmitEvent.data, (data: EventDataMap[EmitEvent.data]) => {
          // 直接返回数据
          resolve(data.value);
        });
      }
    });
  }

  // ============ 私有方法 ============
  private _buildRequest<T extends Operation>(operation: T, params: OperationParamMap[T]) {
    switch (operation) {
      case Operation.READ:
        const readParams = params as OperationParamMap[Operation.READ];
        const typeData = this._encodeType(readParams.type);
        const readBuffer = new ArrayBuffer(11);
        const readView = new DataView(readBuffer);
        readView.setUint8(0, PROTOCOL.COMMAND.REQUEST);
        readView.setUint8(1, PROTOCOL.OPERATION.READ);
        readView.setUint8(2, readParams.length);
        new Uint8Array(readBuffer).set(typeData, 3);
        return readBuffer;
      case Operation.WRITE:
        const writeParams = params as OperationParamMap[Operation.WRITE];
        const writeTypeData = this._encodeType(writeParams.type);
        const buffer = new ArrayBuffer(19);
        const view = new DataView(buffer);
        view.setUint8(0, PROTOCOL.COMMAND.REQUEST);
        view.setUint8(1, PROTOCOL.OPERATION.WRITE);
        view.setUint8(2, writeParams.length);
        new Uint8Array(buffer).set(writeTypeData, 3);
        view.setUint32(11, 0); // 保留字段
        view.setUint32(15, (params as OperationParamMap[Operation.WRITE]).dataLength, false);
        return buffer;
      case Operation.TERMINATE:
        const terParams = params as OperationParamMap[Operation.TERMINATE];
        const terminateBuffer = new ArrayBuffer(4);
        const terminateView = new DataView(terminateBuffer);
        terminateView.setUint8(0, PROTOCOL.COMMAND.REQUEST);
        terminateView.setUint8(1, PROTOCOL.OPERATION.TERMINATE);
        if (terParams.crc) {
          const crc16 = this._calculateCRC16(new Uint8Array(terminateBuffer.slice(0, 2)));
          terminateView.setUint8(2, (crc16 >> 8) & 0xff); // High byte
          terminateView.setUint8(3, crc16 & 0xff); // Low byte
        }
        return terminateBuffer;
      case Operation.DATA_TRANSFER:
        const uplinkParams = params as OperationParamMap[Operation.DATA_TRANSFER];
        const uplinkBuffer = new ArrayBuffer(4 + uplinkParams.data.length);
        const transferView = new DataView(uplinkBuffer);
        transferView.setUint8(0, PROTOCOL.COMMAND.REQUEST);
        transferView.setUint8(1, PROTOCOL.OPERATION.DATA_TRANSFER);
        transferView.setUint8(2, uplinkParams.packetSequence & 0xff);
        transferView.setUint8(3, uplinkParams.dataLength);
        new Uint8Array(uplinkBuffer).set(uplinkParams.data, 4);
        return uplinkBuffer;
      // 终止数据传输接口
      case Operation.DATA_ACK:
        const ackBuffer = new ArrayBuffer(4);
        const ackView = new DataView(ackBuffer);
        ackView.setUint8(0, PROTOCOL.COMMAND.REQUEST);
        ackView.setUint8(1, PROTOCOL.OPERATION.DATA_TRANSFER);
        ackView.setUint8(2, 0x00);
        ackView.setUint8(3, 0x00); // 保留字段
        return ackBuffer;
      case Operation.GET_FLAG:
        const getFlagBuffer = new ArrayBuffer(2);
        const getFlagView = new DataView(getFlagBuffer);
        getFlagView.setUint8(0, PROTOCOL.COMMAND.REQUEST);
        getFlagView.setUint8(1, PROTOCOL.OPERATION.GET_FLAG);
        return getFlagBuffer;
      case Operation.SET_FLAG:
        const setFlagParams = params as OperationParamMap[Operation.SET_FLAG];
        const setFlagBuffer = new ArrayBuffer(6);
        const setFlagView = new DataView(setFlagBuffer);
        setFlagView.setUint8(0, PROTOCOL.COMMAND.REQUEST);
        setFlagView.setUint8(1, PROTOCOL.OPERATION.SET_FLAG);
        setFlagView.setUint32(2, setFlagParams.flag, false); // 升级标志
        return setFlagBuffer;
      case Operation.HOST_AUTH:
        const hostAuthParams = params as OperationParamMap[Operation.HOST_AUTH];
        const hostAuthBuffer = new ArrayBuffer(52); // 1+1+1+1+16+32
        const hostAuthView = new DataView(hostAuthBuffer);
        hostAuthView.setUint8(0, PROTOCOL.COMMAND.RESPONSE); // ??????
        hostAuthView.setUint8(1, PROTOCOL.OPERATION.HOST_AUTH);
        hostAuthView.setUint8(2, hostAuthParams.status);
        if (hostAuthParams.status === AuthStatus.INITIATE) {
          if (!hostAuthParams.keyIndex || !hostAuthParams.random || !hostAuthParams.idCode) {
            throw new Error('Missing required parameters for auth initiation');
          }
          hostAuthView.setUint8(3, hostAuthParams.keyIndex); // Key index
          // Set 16-byte random number (offset 4-19)
          new Uint8Array(hostAuthBuffer, 4, 16).set(hostAuthParams.random);
          // Set 32-byte ID code (offset 20-51)
          const encoder = new TextEncoder();
          const idCodeBytes = encoder.encode(hostAuthParams.idCode);
          if (idCodeBytes.length !== 32) {
            throw new Error('ID code must be exactly 32 bytes');
          }
          new Uint8Array(hostAuthBuffer, 20, 32).set(idCodeBytes);
        } else {
          hostAuthView.setUint8(3, 0); // Unused for non-initiate
          // Zero out the rest of the buffer
          new Uint8Array(hostAuthBuffer, 4, 48).fill(0);
        }
        return hostAuthBuffer;
      case Operation.DEVICE_AUTH:
        const deviceAuthParams = params as OperationParamMap[Operation.DEVICE_AUTH];
        const deviceAuthBuffer = new ArrayBuffer(20); // 1+1+1+16+1
        const deviceAuthView = new DataView(deviceAuthBuffer);
        deviceAuthView.setUint8(0, PROTOCOL.COMMAND.REQUEST);
        deviceAuthView.setUint8(1, PROTOCOL.OPERATION.DEVICE_AUTH);
        deviceAuthView.setUint8(2, deviceAuthParams.status);
        if (deviceAuthParams.status === AuthStatus.INITIATE) {
          if (!deviceAuthParams.random || !deviceAuthParams.keyIndex) {
            throw new Error('Missing required parameters for auth initiation');
          }
          // Set 16-byte random number (offset 3-18)
          new Uint8Array(deviceAuthBuffer, 3, 16).set(deviceAuthParams.random);
          // Set key index (offset 19)
          deviceAuthView.setUint8(19, deviceAuthParams.keyIndex);
        } else {
          // Zero out the random and key index fields
          new Uint8Array(deviceAuthBuffer, 3, 17).fill(0);
        }
        return deviceAuthBuffer;
      default:
    }
  }

  private _encodeType(type: string): Uint8Array {
    // 兼容微信小程序环境的文本编码
    const buffer = this._stringToUint8Array(type);
    const result = new Uint8Array(PROTOCOL.TYPE_LENGTH).fill(0);
    result.set(buffer.subarray(0, PROTOCOL.TYPE_LENGTH));
    return result;
  }

  private _stringToUint8Array(str: string): Uint8Array {
    // 方案1：优先使用TextEncoder（浏览器/Node环境）
    if (typeof TextEncoder !== 'undefined') {
      return new TextEncoder().encode(str);
    }
    // 方案2：微信小程序兼容实现
    const bytes: number[] = [];
    for (let i = 0; i < str.length; i++) {
      const code = str.charCodeAt(i);
      if (code <= 0x7f) {
        bytes.push(code);
      } else if (code <= 0x7ff) {
        bytes.push(0xc0 | (code >> 6));
        bytes.push(0x80 | (code & 0x3f));
      } else if (code <= 0xffff) {
        bytes.push(0xe0 | (code >> 12));
        bytes.push(0x80 | ((code >> 6) & 0x3f));
        bytes.push(0x80 | (code & 0x3f));
      } else {
        bytes.push(0xf0 | (code >> 18));
        bytes.push(0x80 | ((code >> 12) & 0x3f));
        bytes.push(0x80 | ((code >> 6) & 0x3f));
        bytes.push(0x80 | (code & 0x3f));
      }
    }
    return new Uint8Array(bytes);
  }

  private _validateConfiguration(): void {
    if (!this._serviceUUID || !this._characteristicUUID) {
      throw new Error('Bluetooth service not configured');
    }
  }

  private _validateResponseHeader(view: DataView, operation: number): void {
    if (view.getUint8(1) !== operation) {
      console.error(`Invalid operation code: expected ${operation}, got ${view.getUint8(1)}`);
    }
    if (view.getUint8(0) !== PROTOCOL.COMMAND.RESPONSE) {
      throw new Error('Invalid response header');
    }
  }

  private _calculateCRC16(buf: Uint8Array, crcLast: number = 0xffff): number {
    const poly = [0x0000, 0xa001]; // Polynomials for XOR
    let crc = crcLast;

    for (let j = 0; j < buf.length; j++) {
      let ds = buf[j];

      for (let i = 0; i < 8; i++) {
        crc = (crc >> 1) ^ poly[(crc ^ ds) & 1];
        ds = ds >> 1;
      }
    }

    return crc;
  }

  arrayBufferToHexString = (buffer: ArrayBuffer) => {
    return Array.from(new Uint8Array(buffer))
      .map((b) => b.toString(16).padStart(2, '0'))
      .join(' ');
  };

  // private _calculateCRC32(buf: Uint8Array, crcLast: number = 0xffffffff): number {
  //   let crc = ~crcLast; // Initialize with complement of last CRC
  //   for (let i = 0; i < buf.length; i++) {
  //     const byte = buf[i];
  //     crc = crc ^ byte;

  //     for (let j = 7; j >= 0; j--) {
  //       const mask = -(crc & 1);
  //       crc = (crc >>> 1) ^ (0xedb88320 & mask);
  //     }
  //   }
  //   return ~crc >>> 0; // Final complement and convert to unsigned
  // }
}

export class RemoteBluetooth extends BasicRemoteBluetooth {
  async readRemoteControllerVersionInfo(maxRetries = 3, initialDelay = 1000) {
    let retryCount = 0;
    let delay = initialDelay;
    while (retryCount < maxRetries) {
      try {
        // ===== 第一步：启动读请求 =====
        const startResponse = await this.executeCommandWithResponse(Operation.READ, {
          type: RemoteControllerInfoType.VERSION,
          length: 16 // 使用最大块大小
        });

        console.log(`启动读请求成功: ${startResponse}`, this.arrayBufferToHexString(startResponse.value));
        const { status } = this.parseResponse(startResponse.value, Operation.READ);
        console.log(`启动读请求成功，状态码: 0x${status.toString(16)}`);

        if (status !== OperationStatus.SUCCESS) {
          throw new Error(`启动读失败，状态码: 0x${status.toString(16)}`);
        }

        // ===== 第二步：接收完整数据流 =====
        let fullDataResponse: ArrayBuffer;
        try {
          fullDataResponse = await this.readNextResponse(true, true);
          console.log(`读取数据成功: ${fullDataResponse.byteLength} bytes`);
          // 检查数据是否为空
          if (!fullDataResponse || fullDataResponse.byteLength === 0) {
            throw new Error('接收到的数据为空');
          }
        } catch (error) {
          throw new Error(`读取数据失败: ${error.message}`);
        }

        // ===== 第三步：发送终止请求 =====
        const terminateResponse = await this.executeCommandWithResponse(Operation.TERMINATE, {
          crc: false
        });
        const { status: terminateStatus, crc16 } = this.parseResponse(terminateResponse.value, Operation.TERMINATE);
        console.log(`终止请求成功，状态码: 0x${terminateStatus.toString(16)}, CRC16: 0x${crc16.toString(16)}`);
        if (terminateStatus !== OperationStatus.SUCCESS) {
          throw new Error(`终止失败，状态码: 0x${terminateStatus.toString(16)}`);
        }
        console.log('最终数据', this.arrayBufferToHexString(fullDataResponse));
        return this.arrayBufferToHexString(fullDataResponse);
      } catch (error) {
        console.log(error,'error')
        retryCount++;
        console.error(`尝试 ${retryCount}/${maxRetries} 失败: ${error.message}`);
        if (retryCount >= maxRetries) {
          throw new Error(`达到最大重试次数 (${maxRetries}): ${error.message}`);
        }
        // 指数退避
        delay = initialDelay * Math.pow(2, retryCount - 1);
        console.log(`等待 ${delay}ms 后重试...`);
        await new Promise((resolve) => setTimeout(resolve, delay));
      }
    }
    throw new Error('未知错误：重试循环异常结束');
  }
}
